/**
 * GeoSOA/olap4js/CellSet.js
 *
 * CellSet object
 *
 * Copyright (C) 2009 GeoSOA Research Group, Universite Laval
 * See LICENSE file under this distribution for licensing information
 * 
 * @author Etienne Dube
 * @since 20090306
 **/

dojo.provide("GeoSOA.olap4js.CellSet");

dojo.require("GeoSOA.olap4js.AxisMeta");
dojo.require("GeoSOA.olap4js.CellSetAxis");


/**
 * CellSet object
 * 
 * @param {Object} jsonCellSet
 *
 * @constructor
 */
GeoSOA.olap4js.CellSet = function (jsonCellSet) {
	
	this.metadata = new GeoSOA.olap4js.CellSetMetaData(jsonCellSet.metadata, this);
	
	this.axes = [];
	
	for (var i = 0; i < jsonCellSet.axes.length; i++) {
		this.axes[i] = new GeoSOA.olap4js.CellSetAxis(jsonCellSet.axes[i], i, this); 
	}
	
	this.filterAxis = new GeoSOA.olap4js.CellSetAxis(jsonCellSet.filterAxis, -1, this);
	
	this.cells = jsonCellSet.cells;
	
};

// CellSet methods:

/**
 * Return the CellSetAxis object corresponding to the specified ordinal (-1
 * is the filter axis)
 * 
 * @param {Number} ordinal The axis ordinal
 * @return The CellSetAxis object
 * @type Object
 * @public
 */
GeoSOA.olap4js.CellSet.prototype.getAxis = function (ordinal) {
	
	if (ordinal == -1) {
		return this.filterAxis;
	}
	else {
		return this.axes[ordinal];
	}
	
};

/**
 * Returns the dimensionality (number of axes) of this CellSet
 * 
 * @return The number of axes
 * @type Number
 * @public
 */
GeoSOA.olap4js.CellSet.prototype.getNumberOfAxes = function () {
	return this.axes.length;
};


/**
 * Returns the cell corresponding to specified coordinates
 * 
 * @param {Array} coordinates Array of numbers representing the coordinates
 *        of the cell
 * @return The cell if coordinates are valid and within bounds, null otherwise
 * @type Object
 * @public
 */
GeoSOA.olap4js.CellSet.prototype.getCell = function (coordinates) {
	
	if (coordinates instanceof Number) {
		// we have a single ordinal, return the corresponding cell
		return this.cells[coordinates];
	}
	else if (coordinates instanceof Array) {
		// we have coordinates (position ordinals for each axis), retrieve
		// the corresponding cell
		var ordinal = this.coordinatesToOrdinal(coordinates);
		if (ordinal != null) {
			return this.cells[ordinal];
		}
	}
	
	return null;
};


/**
 * Converts coordinates array (e.g. [4, 3, 5]) to cell ordinal.
 * Inspired from Mondrian's
 * mondrian.olap4j.MondrianOlap4jCellSet.coordinatesToOrdinal(List<Integer>)
 * method.
 * 
 * @param {Array} coordinates The coordinates, representing position ordinals
 *        on each axis
 * @return The cell ordinal corresponding to coordinates, or null if
 *         invalid coordinates
 * @type Number
 * @public
 */
GeoSOA.olap4js.CellSet.prototype.coordinatesToOrdinal = function (coordinates) {
	
	if(coordinates.length == this.axes.length) {
		var modulo = 1;
		var ordinal = 0;
		var i;
		
		for (i = 0 ; i < this.axes.length ; i++ ) {
			var axis = this.axes[i];
			var coordinate = coordinates[i];
			if (coordinate < 0 || coordinate >= axis.positions.length) {
				// invalid coordinates
				return null;
			}
			ordinal += coordinate * modulo;
			modulo *= axis.positions.length;
		}
		
		return ordinal;
	}
	
	return null;
};


/**
 * Converts cell ordinal to coordinates array (e.g. [4, 3, 5]).
 * Inspired from Mondrian's
 * mondrian.olap4j.MondrianOlap4jCellSet.ordinalToCoordinateArray(int)
 * method.
 * 
 * @param {Number} ordinal The cell ordinal corresponding to coordinates
 * @return The coordinates, representing position ordinals on each axis,
 *         or null if invalid ordinal 
 * @type Array
 * @public
 */
GeoSOA.olap4js.CellSet.prototype.ordinalToCoordinates = function (ordinal) {
	var coordinates = [];
	var modulo = 1;
	var i;
	for (i = 0; i < this.axes.length; i++) {
		var prevModulo = modulo;
		modulo *= this.axes[i].positions.length;
		coordinates[i] = (ordinal % modulo) / prevModulo;
	}
	if (ordinal < 0 || ordinal >= modulo) {
		// invalid ordinal, outside the CellSet bounds
		return null;
	}
	return coordinates;
};




/**
 * CellSetMetaData object
 * 
 * @param {Object} jsonMetadata
 * @param {Object} cellSet CellSet object this CellSetMetaData belongs to
 *
 * @constructor
 */
 GeoSOA.olap4js.CellSetMetaData = function (jsonMetadata, cellSet) {

	this.cellSet = cellSet;
	this.executedQuery = jsonMetadata.executedQuery;
	this.cubeName = jsonMetadata.cubeName;

	this.axesMeta = [];
	for (var i = 0; i < jsonMetadata.axesMeta.length; i++) {
		this.axesMeta[i] = new GeoSOA.olap4js.AxisMeta(jsonMetadata.axesMeta[i], i, this);
	}
	
	this.filterAxisMeta = new GeoSOA.olap4js.AxisMeta(jsonMetadata.filterAxisMeta, -1, this);
	
};

// CellSetMetaData methods:

/**
 * Returns the AxisMeta object corresponding to the specified ordinal (-1
 * is the filter axis metadata)
 * 
 * @param {Number} ordinal The axis ordinal
 * @return The AxisMeta object
 * @type Object
 * @public
 */
GeoSOA.olap4js.CellSetMetaData.prototype.getAxisMeta = function (ordinal) {
	
	if (ordinal == -1) {
		return this.filterAxisMeta;
	}
	else {
		return this.axesMeta[ordinal];
	}
	
};
